#include "reconnectcommand.h"

#include <sstream>
using std::ostringstream;
using std::istringstream;

#include <Poco/DOM/Document.h>
#include <Poco/DOM/Element.h>
#include <Poco/DOM/NodeList.h>
#include <Poco/DOM/DOMWriter.h>
#include <Poco/DOM/DOMParser.h>
#include <Poco/DOM/Text.h>
using Poco::XML::Document;
using Poco::XML::Element;
using Poco::XML::NodeList;
using Poco::XML::DOMWriter;
using Poco::XML::DOMParser;
using Poco::XML::Text;

#include <Poco/Net/IPAddress.h>
using Poco::Net::IPAddress;

namespace Commands{
	const string ReconnectCommand::NAME = "Reconnect";

	ReconnectCommand::ReconnectCommand(SocketAddress reconAddr, id_type id, NodeData* nodedata) 
		: NodeCommand(nodedata, true)
		  , _prevId(id)
		  , _reconAddr(reconAddr)
	{
		cout << "ReconnectCommand :: NodeData.local :: " << local().toString() << endl;
		cout << "ReconnectCommand :: NodeData.remote :: " << remote().toString() << endl;
	}
	
	ReconnectCommand::~ReconnectCommand()
	{}
	
	void ReconnectCommand::execute()
	{
		if (_reconAddr.host().isWildcard())
		{
			cout << "ReconnectCommand :: Changing " << _reconAddr.toString() << " to ";
			_reconAddr = SocketAddress(remote().host(), _reconAddr.port());
			cout << _reconAddr.toString() << endl;
		}
		
		cout << "ReconnectCommand :: Executing" << endl;
		if (nodeId() == _prevId)
		{
			cout << "ReconnectCommand :: We have arrived" << endl;
			if (local().host() != remote().host())
			{
				if (master()->getConnectedAddress().host() != remote().host())
				{
					cout << "ReconnectCommand :: Leaving the slave" << endl;
					master()->leave();
					cout << "ReconnectCommand :: Going to join #1 " << _reconAddr.toString() << endl;
					node()->join(_reconAddr.host().toString(), _reconAddr.port());
				} else {
					if (_reconAddr.host() == local().host())
					{
						cout << "ReconnectCommand :: Leaving the slave" << endl;
						master()->leave();
						cout << "ReconnectCommand :: Going to join #2 " << _reconAddr.toString() << endl;
						master()->join(_reconAddr.host().toString(), _reconAddr.port());
						node()->setPrevId(nodeId());
					} else {
						cout << "ReconnectCommand :: Leaving the slave" << endl;
						master()->leave();
						cout << "ReconnectCommand :: Going to join #1 " << _reconAddr.toString() << endl;
						node()->join(_reconAddr.host().toString(), _reconAddr.port());
					}
				}
			}
			cout << "ReconnectCommand :: setting result" << endl;
			setResult("OK");
		} else {
			cout << "ReconnectCommand :: Going to forward myself" << endl;
			master()->executeCommand(this);
		}
		cout << "ReconnectCommand :: Done!" << endl;
	}
	
	string ReconnectCommand::name()
	{
		return string(ReconnectCommand::NAME);
	}
			
	string ReconnectCommand::repr(){
		ostringstream out;
		DOMWriter w;
		w.writeNode(out, toXML());
		return out.str();

	}

	Document* ReconnectCommand::toXML(Document* )
	{
		Document* doc = new Document();
		Element* nnode = doc->createElement("new-node");
		nnode->setAttribute("address", _reconAddr.toString());
		doc->appendChild(nnode);

		Element* dest = doc->createElement("destination");
		ostringstream ss; ss << _prevId;
		dest->setAttribute("node-id", ss.str());
		doc->appendChild(dest);

		return NodeCommand::toXML(doc);
	}
			
	CommandCreator ReconnectCommand::creator(){
		return ReconnectCommand::makeCommand;
	}
	
	Command* ReconnectCommand::makeCommand(string r, void* d)
	{
		NodeData* ndata = (NodeData*) d;
		DOMParser parser;
		Document* doc = parser.parseString(r);

		SocketAddress addr(((Element*)(doc->getElementsByTagName("new-node")->item(0)))->getAttribute("address"));

		id_type pid;
		istringstream(((Element*)(doc->getElementsByTagName("destination")->item(0)))->getAttribute("node-id")) >> pid;

		return new ReconnectCommand(addr, pid, ndata);

	}
}

